package club.vann.kafka.mock.common.model.info;

import club.vann.kafka.mock.common.model.mapper.FrontEndTableMapper;
import com.cyberx.lunyu.TableDataOuterClass;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.Data;
import lombok.Getter;
import org.apache.commons.lang.ArrayUtils;

/** 动态表格数据， 返回列名定义 返回行数据目前行数据只支持string和double，时间戳 */
@Data
public class FrontEndTable {
  /** 列头定义 */
  List<Column> columns;

  /** 列名[唯一]映射Column对象map，用来查找columnName位于第几列 */
  Map<String, Column> columnMap;

  /** 动态表格名称，确定表格的唯一性，作为发送kafka消息的key */
  String tableName;

  /** 动态表格类型，某一类型的动态表格可以归为一类 */
  String tableType;

  /** 行数据 */
  List<Row> rows;

  /** 时间戳 */
  Long timestamp;

  public FrontEndTable() {}

  public FrontEndTable(
      String tableName, String tableType, List<Column> columns, List<Row> rows, Long timestamp) {
    this.tableName = tableName;
    this.tableType = tableType;
    this.columns = columns;
    this.columnMap =
        this.columns.stream().collect(Collectors.toMap(Column::columnName, Function.identity()));
    this.rows = rows;
    this.timestamp = timestamp;
  }

  /**
   * 获取具体某一个单元格中的数据
   *
   * @param rowNum 行号
   * @param columnNum 列号
   */
  public Optional<Object> getCellData(int rowNum, int columnNum) {
    Object value = null;
    if (this.rows.size() > rowNum
        && this.rows.get(rowNum) != null
        && this.rows.get(rowNum).data() != null
        && this.rows.get(rowNum).data().length > columnNum) {
      value = this.rows.get(rowNum).data()[columnNum];
    }
    return Optional.ofNullable(value);
  }

  /** 查找columnName位于第几列 */
  public int getColumnIndex(String columnName) {
    Column column = this.columnMap.get(columnName);
    int index = -1;
    if (column != null) {
      index = ArrayUtils.indexOf(this.columns.toArray(new Column[] {}), column);
    }
    return index;
  }

  /**
   * 动态表格列定义
   *
   * @param columnName 列名
   * @param columnTitle 列显示名称
   * @param style 样式类型
   * @param sort 是否需要排序
   * @param filter 是否需要过滤
   * @param dataType 数据类型,目前支持double/string/时间戳
   */
  public record Column(
      String columnName,
      String columnTitle,
      String style,
      boolean sort,
      boolean filter,
      DataType dataType) {}

  /** 动态表格行数据 */
  public record Row(Object[] data) {
    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
      Row row = (Row) o;
      return Arrays.equals(data, row.data);
    }

    @Override
    public int hashCode() {
      return Arrays.hashCode(data);
    }

    @Override
    public String toString() {
      return "Row{" + "data=" + Arrays.toString(data) + '}';
    }
  }

  /** 列数据类型枚举 */
  public enum DataType {
    STRING(0, String.class),
    DOUBLE(1, Double.class),
    TIMESTAMP(2, Long.class);

    @Getter private final int code;
    @Getter private final Class<?> name;
    private static final Map<Integer, DataType> codeMapping;
    private static final Map<Class<?>, DataType> nameMapping;

    static {
      codeMapping = new HashMap<>();
      nameMapping = new HashMap<>();
      for (DataType orderRemarkType : values()) {
        codeMapping.put(orderRemarkType.getCode(), orderRemarkType);
        nameMapping.put(orderRemarkType.getName(), orderRemarkType);
      }
    }

    DataType(int code, Class<?> name) {
      this.code = code;
      this.name = name;
    }

    public static Optional<DataType> getByCode(int code) {
      return Optional.ofNullable(codeMapping.get(code));
    }

    public static Optional<DataType> getByName(Class<?> name) {
      return Optional.ofNullable(nameMapping.get(name));
    }
  }

  public static FrontEndTable convert(TableDataOuterClass.TableData tableData) {
    FrontEndTableMapper instance = FrontEndTableMapper.INSTANCE;
    FrontEndTable frontEndTable = instance.convert(tableData);
    List<Row> rows =
        FrontEndTableMapper.rowListToRowArray(tableData.getColumnList(), tableData.getRowList());
    frontEndTable.setRows(rows);
    return frontEndTable;
  }
}
